<?php
/**
 * @version $Id: SURBL.php 181 2008-03-11 21:46:28Z mikhail.miguel $
 */

require_once dirname(dirname(dirname(dirname(__FILE__)))).'/lib/trackback/spamcheck.php';
require_once dirname(dirname(dirname(dirname(__FILE__)))).'/lib/trackback/lookupbl.php';

class SpamCheck_SURBL extends SpamCheck {

    var $_options = array(
        'sources'       => array(
            'surbl' => array(            
                'url.rbl.jp',
                'multi.surbl.org'
            ),
            'dnsbl' => array()
        ),
        'elements'      => array(
            'url',
            'title',
            'excerpt',
        )
    );

    var $_surbl;
    var $_urls = array();
    var $_domains = array();
    var $_ips = array();
    var $_doubleCcTldFile = 'http://spamcheck.freeapp.net/two-level-tlds';

    function SpamCheck_SURBL($options=null)
    {
        if (is_array($options)) {
            foreach ($options as $key => $val) {
                $this->_options[$key] = $val;
            }
        }
        $this->_surbl = new LookupBlacklist();
    }

    function check($trackback)
    {
        $result = $this->_checkSource($this->_options['sources'], $trackback);
        if(TrackbackService::isError($result)) {
            $this->_results = true;
            return $result;
        }
        return false;
    }
    
    function _checkSource(&$sources, $trackback)
    {
        if(TrackbackService::isError($res = $this->_extractURLs($trackback))) {
            return $res;
        }
        if(count($this->_urls) == 0) {
            return false;
        }
        
        // strip domain prefix to get a domain 
        $this->buildLookupDomain();

        // check if a domain is ip address
        if(count($this->_ips)) {
            if($this->_surbl->setBlacklists($sources['dnsbl'])) {
                foreach($this->_ips as $ip) {
                    if($this->_surbl->isListed($this->_surbl->reverseIP($ip))) {
                        return TrackbackService::setError('Your contents are contaminated with spammy domain name:'.htmlspecialchars($ip), ENT_QUOTES);
                    }
                }
            }
        }
        // check if domains exist
        if(count($this->_domains)) {
            if($this->_surbl->setBlacklists($sources['surbl'])) {
                foreach($this->_domains as $domain) {
                    if($this->_surbl->isListed($domain)) {
                        return TrackbackService::setError('Your contents are contaminated with spammy domain name:'.htmlspecialchars($domain), ENT_QUOTES);
                    }
                }
            }
        }   
        return false;   
    }

    function _extractURLs($trackback)
    {
        $urls = '(?:http|file|ftp)';
        $ltrs = 'a-z0-9';
        $gunk = '.-';
        $punc = $gunk;
        $any = "$ltrs$gunk";
        $regex = "{
                      $urls   ://
                      [$any]+


                      (?=
                        [$punc]*
                        [^$any]
                      |
                      )
                  }x";
        
        foreach ($this->_options['elements'] as $element) {
            if(TrackbackService::isError($item = $trackback->get($element))) {
                return TrackbackService::setError($item->getMessage().' Cannot procede further without it.');
            }
            if (0 !== preg_match_all($regex, $item, $matches)) {
                foreach ($matches[0] as $match) {
                    $this->_urls[] = $match;
                }
            }
        }
        $this->_urls = array_unique($this->_urls);
        return true;
    }

    function buildLookupDomain() {
        foreach($this->_urls as $url) {
            // extract the hostname from the given URI
            $parsed_url = parse_url($url);
            $host = $parsed_url['host'];

            // check if the "hostname" is an ip
            if($this->_surbl->checkIP($host)) {
                $this->_ips[] = $this->_surbl->reverseIP($host);
            } else {
                $this->_domains[] = $this->_stripDomainPrefixes($host);
            }
        }
        $this->_ips = array_unique($this->_ips);
        $this->_domains = array_unique($this->_domains);
    }

    function _stripDomainPrefixes($host) {
        static $doubleLevelTlds = array();
        if (empty($doubleLevelTlds)) {
            $doubleLevelTlds = $this->_getDoubleCcTld();
        }

        $host_elements = explode('.', $host);
        while (count($host_elements) > 3) {
            array_shift($host_elements);
        }
        $host_3_elements = implode('.', $host_elements);
        
        $host_elements = explode('.', $host);
        while (count($host_elements) > 2) {
            array_shift($host_elements);
        }
        $host_2_elements = implode('.', $host_elements);
            
        // check if is in "CC-2-level-TLD"
        return (array_key_exists($host_2_elements, $doubleLevelTlds))? $host_3_elements : $host_2_elements;
    }


    function _getDoubleCcTld()
    {
        $duration = 2592000;    // 30 days
        $cache_path = XOOPS_TRUST_PATH . '/cache';
        $cache_file = $cache_path.'/two_level_tld_'.substr(md5($this->_doubleCcTldFile), 0, 12);
        $cache_file_mtime = file_exists($cache_file) ? filemtime($cache_file) : 0 ;
        if(!file_exists($cache_file) || $cache_file_mtime < time() - $duration) {
            $contents = TrackbackService::getContent($this->_doubleCcTldFile);
            if (TrackbackService::isError($contents)) {
                return $contents;
            }
            $fp = fopen($cache_file, 'wb');
            if(!$fp) return array();
            fwrite($fp, $contents) ;
            fclose($fp) ;
            
        } else {
            $fp = fopen($cache_file, 'rb');
            $contents = fread($fp, filesize($cache_file));
            fclose($fp);
        }

        $data = explode("\n", $contents);
        return array_flip($data);        
    }

}
?>